package com.glsc.ngateway.platform.service.easyflow;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.glsc.ngateway.common.api.common.utils.CommonUtils;
import com.glsc.ngateway.common.api.flowable.dto.CurrentTaskInfoDto;
import com.glsc.ngateway.common.api.flowable.resp.FlowableResponse;
import com.glsc.ngateway.common.api.flowable.resp.ResponseMsgDto;
import com.glsc.ngateway.common.api.flowable.resp.ResponseSituationDto;
import com.glsc.ngateway.common.api.shortmsg.bo.MessageGenericVo;
import com.glsc.ngateway.common.api.shortmsg.feign.IFeignShortmsgService;
import com.glsc.ngateway.common.base.domain.mysql.gateway.other.SystemConfig;
import com.glsc.ngateway.common.base.domain.mysql.gateway.platform.ProcessEditConfig;
import com.glsc.ngateway.common.base.domain.mysql.gateway.platform.easyflow.BaseFlowForm;
import com.glsc.ngateway.platform.service.FlowCommonService;
import com.glsc.ngateway.platform.service.MailService;
import com.glsc.ngateway.platform.service.SystemConfigService;
import com.glsc.ngateway.platform.service.UserService;
import com.glsc.ngateway.platform.service.easyflow.dto.FlowDataDto;
import com.glsc.ngateway.platform.service.easyflow.dto.WorkflowMsgDto;
import com.glsc.ngateway.platform.service.itflow.config.ProcessEditConfigService;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.text.MessageFormat;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 流程任务生成、任务审核通过前、审核后通用处理类
 *
 * @author zzp
 */
@Slf4j
public abstract class AbstractEasyFlowTaskHandle<TYPEFORM extends BaseFlowForm> {

    @Resource
    private FlowCommonService flowableService;

    @Resource
    private MailService mailService;

    @Resource
    private ProcessEditConfigService processEditConfigService;

    @Resource
    private UserService userService;

    @Resource
    private IFeignShortmsgService shortmsgService;

    @Resource
    private SystemConfigService configService;

    /**
     * 任务任务处理
     */
    protected Map<String, Consumer<FlowDataDto>> taskCreateHandleMap = new HashMap<>();
    protected Map<String, Consumer<FlowDataDto>> taskPassBeforeHandleMap = new HashMap<>();
    protected Map<String, Consumer<FlowDataDto>> taskPassAfterHandleMap = new HashMap<>();
    protected Map<String, Consumer<FlowDataDto>> taskRejectBeforeHandleMap = new HashMap<>();
    protected Map<String, Consumer<FlowDataDto>> taskRejectAfterHandleMap = new HashMap<>();

    /**
     * 流程类型定义【重要区分】
     */
    public abstract String getProcessDefineKey();

    /**
     * 流程创建前的前序处理，一般是初始化xxGroup等初始化数据
     */
    public abstract void handleProcessBeforeCreate(@NotNull FlowDataDto<TYPEFORM> flowDataDto);


    /**
     * 节点审核通过前个性化节点处理，一般是检查附件是否齐全、是否符合要求等
     */
    public void handleTaskPassBefore(@NotNull FlowDataDto<TYPEFORM> flowDataDto) {
        Consumer<FlowDataDto> remindMethod = taskPassBeforeHandleMap.get(flowDataDto.getTaskDefinitionKey());
        if (remindMethod == null) {
            log.debug("【任务审核前处理】未找到，任务标识={}，说明不需要个性化处理", flowDataDto.getTaskDefinitionKey());
        } else {
            remindMethod.accept(flowDataDto);
        }
    }

    /**
     * 节点审核通过后个性化节点处理，一般是发送邮件通知等个性化节点处理
     */
    public void handleTaskRejectBefore(@NotNull FlowDataDto<TYPEFORM> flowDataDto) {
        Consumer<FlowDataDto> remindMethod = taskRejectBeforeHandleMap.get(flowDataDto.getTaskDefinitionKey());
        if (remindMethod == null) {
            log.info("【任务退回前处理】未找到，任务标识={}，说明不需要个性化处理", flowDataDto.getTaskDefinitionKey());
        } else {
            remindMethod.accept(flowDataDto);
        }
    }

    /**
     * 任务生成的默认处理
     * * *
     */
    public void defaultTaskCreateHandle(@NotNull WorkflowMsgDto msgDto) {

    }

    //各类后处理，以消息队列方式处理

    /**
     * 通用调用入口
     */
    public void handleTaskRejectAfter(@NotNull WorkflowMsgDto msgDto) {
        //流程处理后任务通知
        this.handleTaskPassAfterRemindMessage(msgDto);

        Consumer<FlowDataDto> remindMethod = taskRejectAfterHandleMap.get(msgDto.getTaskDefinitionKey());
        if (remindMethod == null) {
            log.info("【任务退回后处理】未找到，任务标识={}，说明不需要个性化处理", msgDto.getTaskDefinitionKey());
        } else {
            if (ObjectUtil.isNotNull(msgDto.getAfterFlowDataDto())) {
                remindMethod.accept(msgDto.getAfterFlowDataDto());
            }
        }
    }

    /**
     * 消息方式处理：通用任务生成调用入口
     */
    public void handleTaskCreate(@NotNull WorkflowMsgDto msgDto) {
        Consumer<FlowDataDto> remindMethod = taskCreateHandleMap.get(msgDto.getTaskDefinitionKey());
        if (remindMethod == null) {
            log.info("【任务生成后处理】未找到，任务标识={}，说明不需要个性化处理", msgDto.getTaskDefinitionKey());

            log.info("【任务生成后处理】尝试使用默认的处理");
            this.defaultTaskCreateHandle(msgDto);//【重要】任务生成会使用默认的调用逻辑
        } else {
            if (ObjectUtil.isNotNull(msgDto.getAfterFlowDataDto())) {
                remindMethod.accept(msgDto.getAfterFlowDataDto());
            }
        }
    }

    /**
     * 消息方式处理：通用任务生成调用入口
     */
    public void handleTaskPassAfter(@NotNull WorkflowMsgDto msgDto) {
        //流程处理后任务通知
        this.handleTaskPassAfterRemindMessage(msgDto);

        Consumer<FlowDataDto> remindMethod = taskPassAfterHandleMap.get(msgDto.getTaskDefinitionKey());
        if (remindMethod == null) {
            log.info("【任务审核后处理】未找到，任务标识={}，说明不需要个性化处理", msgDto.getTaskDefinitionKey());
        } else {
            if (ObjectUtil.isNotNull(msgDto.getAfterFlowDataDto())) {
                remindMethod.accept(msgDto.getAfterFlowDataDto());
            }
        }
    }


    /**
     * 任务生成的默认处理
     * * *
     */
    public void handleProcessViewAfter(@NotNull WorkflowMsgDto msgDto) {
        try {
            //流程已经查看，抄送任务的已读状态修改
            flowableService.copyProcessRead(msgDto.getProcessId(), msgDto.getOperatorAccount());
        } catch (Exception e) {
            String errMsg = String.format("流程-%s，流程查看节点对象后续处理任务异常", msgDto.getProcessId());

            mailService.sendMailToAdmin("流程查看节点对象后续处理任务异常", errMsg);

            log.error(errMsg, e);
        }
    }

    /**
     * 流程节点参考处理方法
     */
    public void taskHandleDemo(@NotNull FlowDataDto flowDataDto) {
        log.info("流程节点处理：{}", flowDataDto.getTaskDefinitionKey());
    }


    /**
     * 任务提交后，新增任务则邮件、企微等通知
     **/
    public void handleTaskPassAfterRemindMessage(@NotNull WorkflowMsgDto msgDto) {

        //流程审核前任务
        Map<String, CurrentTaskInfoDto> beforeAuditFlowTaskMap = new HashMap<>();
        if (CollUtil.isNotEmpty(msgDto.getBeforeAuditFlowTaskList())) {
            beforeAuditFlowTaskMap = msgDto.getBeforeAuditFlowTaskList().stream().collect(Collectors.toMap(CurrentTaskInfoDto::getCurrentTaskDefinitionKey, Function.identity()));
        }

        //流程审核后任务列表
        List<CurrentTaskInfoDto> afterAuditFlowTaskList = msgDto.getAfterAuditFlowTaskList();
        if (CollectionUtil.isEmpty(afterAuditFlowTaskList)) {
            afterAuditFlowTaskList = new ArrayList<>();
        }

        //当前流程信息
        ResponseSituationDto afterAuditFlowInfo = flowableService.getProcessInfo(msgDto.getProcessId());

        //任务配置项及任务说明
        List<ProcessEditConfig> processEditConfigList = processEditConfigService.findAllByProcessType(this.getProcessDefineKey());
        Map<String, ProcessEditConfig> processEditConfigMap = processEditConfigList.stream().collect(Collectors.toMap(ProcessEditConfig::getNodeType, Function.identity()));
        //审核后的新任务，则酌情增加个性化待办提示【根据节点不同，个性化处理】
        try {
            //后续节点发起时邮件提醒--待办-------------------------------------------------
            for (CurrentTaskInfoDto taskInfoDto : afterAuditFlowTaskList) {
                String taskDefinitionKey = taskInfoDto.getCurrentTaskDefinitionKey();
                if (!beforeAuditFlowTaskMap.containsKey(taskDefinitionKey)) {
                    String taskDesc = (processEditConfigMap.get(taskDefinitionKey) == null ||
                            StrUtil.isEmpty(processEditConfigMap.get(taskDefinitionKey).getTaskDescription())) ? "无" : processEditConfigMap.get(taskDefinitionKey).getTaskDescription();
                    //调用通用通知服务
                    this.commonNewTaskRemind(afterAuditFlowInfo.getProcessTitle(), taskInfoDto.getCurrentTaskName()
                            , taskDesc, taskInfoDto.getCurrentAssigneeList(), Arrays.asList("mail", "wechat"));
                }
            }
            //该节点通过时,通知当前节点其余用户
            try {
                if (CollUtil.isNotEmpty(msgDto.getBeforeAuditFlowTaskList())) {
                    List<String> copyAccountList = new ArrayList<>();
                    //即除了当前任务，如果处理后任务不存在了，意味着当前节点多个人处理，其余一个人处理了，其余人员增加抄送
                    for (CurrentTaskInfoDto currentTaskInfoDto : msgDto.getBeforeAuditFlowTaskList()) {
                        if (msgDto.getTaskDefinitionKey().equalsIgnoreCase(currentTaskInfoDto.getCurrentTaskDefinitionKey())) {
                            List<String> sameTaskAssigneeList = currentTaskInfoDto.getCurrentAssigneeList();
                            for (String assignee : sameTaskAssigneeList) {
                                if (!StrUtil.equals(msgDto.getOperatorAccount(), assignee)) {
                                    copyAccountList.add(assignee);
                                }
                            }
                        }
                    }
                    log.info("流程{}-{}-任务完成-{}-任务推送处理人{}-实际操作处理人{}，增加同角色的其余人都转至待阅事项,{}"
                            , msgDto.getProcessId(), msgDto.getTaskDefinitionKey()
                            , msgDto.getCurrentTaskName()
                            , msgDto.getOperatorAccount()
                            , msgDto.getOperatorAccount()
                            , String.join(",", copyAccountList));
                    if (CollectionUtil.isNotEmpty(copyAccountList)) {
                        String remindStr = msgDto.getCurrentTaskName() + "同岗位其它人员已经处理";
                        FlowableResponse<ResponseMsgDto> response = flowableService.copyProcess(msgDto.getProcessId(), copyAccountList, msgDto.getOperatorAccount(), remindStr);
                    }
                }
            } catch (Exception e) {
                String errMsg = String.format("流程-%s-%s，流程处理完毕，待办如同角色已一个完成，同角色的其余人都转至待阅事项。异常",
                        msgDto.getProcessId(), msgDto.getCurrentTaskName());
                log.error(errMsg, e);
                mailService.sendMailToAdmin("流程审核异常", errMsg + CommonUtils.getString(e.getMessage(), ""));
            }

        } catch (Exception e) {
            log.error("流程通知异常!", e);
            String errMsg = String.format("流程-%s-%s，附加通知或附加处理异常",
                    afterAuditFlowInfo.getProcessTitle(), afterAuditFlowInfo.getProcessId());
            mailService.sendMailToAdmin("国联技术服务平台流程处理异常", errMsg +
                    CommonUtils.getString(e.getMessage(), ""));
            log.error(errMsg, e);
        }
    }

    /**
     * 发送任务待办提醒
     */
    private void commonNewTaskRemind(String processTitle, String taskName, String taskDescription, List<String> accountList, List<String> remindTypeList) {
        //收件人邮箱
        List<String> recvMailList = userService.getUserEmailListByAccountList(accountList);
        if (CollectionUtil.isEmpty(recvMailList)) {
            String errMsg = String.format("【%s】流程任务-【%s】提醒未找到接收人信息，无法进行提醒通知", processTitle, taskName);
            log.error(errMsg);
            return;
        }

        //发送邮件
        String mailTitle = MessageFormat.format("【待办】-【{0}】-【{1}】", processTitle, taskName);
        String mailContent = MessageFormat.format("【<span style=\"font-weight: bolder;\">{0}</span>】国联技术服务平台流程任务，烦请至国联技术服务平台系统内处理【<span style=\"font-weight: bolder;\">{1}</span>】相关事宜。</br>任务说明：{2}",
                processTitle, taskName, StrUtil.isEmpty(taskDescription) ? "无" : taskDescription);

        if (remindTypeList.contains("mail")) {//邮件
            mailService.sendMail(recvMailList.toArray(new String[recvMailList.size()]), null, mailTitle, mailContent, null);
        }

        if (remindTypeList.contains("wechat")) {//企微
            SystemConfig agentIdConfig = configService.findByCode("WECHAT_GATEWAY_APP_AGENTID");
            if (Objects.isNull(agentIdConfig) || org.apache.logging.log4j.util.Strings.isBlank(agentIdConfig.getConfigValue())) {
                log.error("需求流程提醒异常：缺少配置WECHAT_GATEWAY_APP_AGENTID");
            }

            shortmsgService.sendAgentMessageGeneric(MessageGenericVo.builder()
                    .oaUserAccounts(accountList)
                    .agentId(agentIdConfig.getConfigValue())
                    .textContent("【国联技术服务平台提醒】" + mailTitle)
                    .build());
        }
    }
}
